import webview
import json
import threading
import time
import socket
import subprocess
import sys
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
import logging
import sqlite3
import atexit

# 配置日志
logging.basicConfig(
    level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)


class PyWebViewApi:
    def __init__(self):
        self.db_file = "monitor.db"
        self.config_data = self.load_config()
        self.lock = threading.Lock()
        self.stop_event = threading.Event()
        self.max_workers = 20
        self.interval = 5  # 默认检测间隔(秒)
        self.stats = {}
        self.monitor_thread = None
        self.paused_items = set()  # 暂停的项目集合

        # 缓存项目ID映射，避免频繁查询数据库
        self.item_id_cache = {}  # {(ip, port, description): id}

        # 初始化数据库
        self.init_database()

        # 初始化统计数据
        self.init_stats()

        # 注册程序退出时的清理函数
        atexit.register(self.cleanup)

        # 启动监控线程
        self.start_monitoring()

    def init_database(self):
        """初始化数据库"""
        conn = sqlite3.connect(self.db_file)
        cursor = conn.cursor()

        # 创建监控项表
        cursor.execute(
            """
            CREATE TABLE IF NOT EXISTS monitor_items (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                ip TEXT NOT NULL,
                port TEXT,
                description TEXT,
                enabled INTEGER DEFAULT 1
            )
        """
        )

        # 为提高查询效率，创建索引
        cursor.execute(
            """
            CREATE INDEX IF NOT EXISTS idx_ip_port_desc 
            ON monitor_items (ip, port, description)
        """
        )

        conn.commit()
        conn.close()

    def init_stats(self):
        """初始化统计数据"""
        self.stats = {}
        for idx, item in enumerate(self.config_data):
            key = str(idx + 1)
            self.stats[key] = {
                "success": 0,
                "failure": 0,
                "last_execution": "未执行",
                "port": item.get("port", ""),
                "check_method": "Socket" if item.get("port") else "Ping",
                "desc": item.get("desc", ""),
                "ip": item["IP"],
                "latest_result": "待检测",
                "success_rate": 0,
            }

    def load_config(self):
        """从数据库加载配置"""
        try:
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()

            cursor.execute(
                "SELECT ip, port, description FROM monitor_items WHERE enabled = 1"
            )
            rows = cursor.fetchall()

            config = []
            for row in rows:
                item = {"IP": row[0], "port": row[1] if row[1] else "", "desc": row[2]}
                config.append(item)

            conn.close()
            return config
        except Exception as e:
            logger.error(f"加载配置失败: {str(e)}")
            return []

    def get_all_items(self):
        """获取所有监控项"""
        try:
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()

            cursor.execute(
                "SELECT id, ip, port, description, enabled FROM monitor_items"
            )
            rows = cursor.fetchall()

            items = []
            for row in rows:
                item = {
                    "id": row[0],
                    "ip": row[1],
                    "port": row[2] if row[2] else "",
                    "desc": row[3],
                    "enabled": bool(row[4]),
                }
                items.append(item)

                # 更新缓存
                cache_key = (row[1], row[2] if row[2] else "", row[3])
                self.item_id_cache[cache_key] = row[0]

            conn.close()
            return items
        except Exception as e:
            logger.error(f"获取监控项失败: {str(e)}")
            return []

    def add_item(self, ip, port, description):
        """添加监控项"""
        try:
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()

            cursor.execute(
                """
                INSERT INTO monitor_items (ip, port, description, enabled)
                VALUES (?, ?, ?, ?)
            """,
                (ip, port, description, 1),
            )

            item_id = cursor.lastrowid
            conn.commit()
            conn.close()

            # 更新缓存
            cache_key = (ip, port, description)
            self.item_id_cache[cache_key] = item_id

            # 重新加载配置和统计数据
            self.config_data = self.load_config()
            self.init_stats()

            return {"status": "success", "message": "添加成功"}
        except Exception as e:
            logger.error(f"添加监控项失败: {str(e)}")
            return {"status": "error", "message": f"添加失败: {str(e)}"}

    def delete_items(self, ids):
        """删除监控项"""
        try:
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()

            placeholders = ",".join("?" * len(ids))
            cursor.execute(
                f"DELETE FROM monitor_items WHERE id IN ({placeholders})", ids
            )

            conn.commit()
            conn.close()

            # 清理缓存和暂停列表中的相关条目
            keys_to_remove = []
            for key, item_id in self.item_id_cache.items():
                if item_id in ids:
                    keys_to_remove.append(key)

            for key in keys_to_remove:
                del self.item_id_cache[key]

            for item_id in ids:
                self.paused_items.discard(item_id)

            # 重新加载配置和统计数据
            self.config_data = self.load_config()
            self.init_stats()

            return {"status": "success", "message": "删除成功"}
        except Exception as e:
            logger.error(f"删除监控项失败: {str(e)}")
            return {"status": "error", "message": f"删除失败: {str(e)}"}

    def toggle_items(self, ids, enabled):
        """启用/禁用监控项"""
        try:
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()

            placeholders = ",".join("?" * len(ids))
            cursor.execute(
                f"UPDATE monitor_items SET enabled = ? WHERE id IN ({placeholders})",
                [enabled] + ids,
            )

            conn.commit()
            conn.close()

            # 重新加载配置和统计数据
            self.config_data = self.load_config()
            self.init_stats()

            status_text = "启用" if enabled else "禁用"
            return {"status": "success", "message": f"{status_text}成功"}
        except Exception as e:
            logger.error(f"更新监控项状态失败: {str(e)}")
            status_text = "启用" if enabled else "禁用"
            return {"status": "error", "message": f"{status_text}失败: {str(e)}"}

    def pause_items(self, ids):
        """暂停监控项"""
        try:
            for item_id in ids:
                self.paused_items.add(item_id)
            return {"status": "success", "message": "项目已暂停"}
        except Exception as e:
            logger.error(f"暂停监控项失败: {str(e)}")
            return {"status": "error", "message": f"暂停失败: {str(e)}"}

    def resume_items(self, ids):
        """恢复监控项"""
        try:
            for item_id in ids:
                self.paused_items.discard(item_id)
            return {"status": "success", "message": "项目已恢复"}
        except Exception as e:
            logger.error(f"恢复监控项失败: {str(e)}")
            return {"status": "error", "message": f"恢复失败: {str(e)}"}

    def execute_items_now(self, ids=None):
        """立即执行监控项"""
        try:
            # 检查是否应该停止
            if self.stop_event.is_set():
                return {"status": "error", "message": "监控已停止"}

            # 获取所有项目
            all_items = self.get_all_items()

            # 如果没有指定ID，则执行所有启用的项目
            if ids is None:
                items_to_execute = [item for item in all_items if item["enabled"]]
            else:
                # 只执行指定的项目（且必须是启用的）
                items_to_execute = [
                    item for item in all_items if item["id"] in ids and item["enabled"]
                ]

            # 使用线程池执行检测任务
            results = []
            with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
                # 检查是否应该停止
                if self.stop_event.is_set():
                    return {"status": "error", "message": "监控已停止"}

                futures = {}
                for item in items_to_execute:
                    # 检查是否应该停止
                    if self.stop_event.is_set():
                        break

                    ip = item["ip"]
                    port = item["port"]
                    desc = item["desc"]

                    # 提交任务
                    future = executor.submit(self.check_connection, ip, port)
                    futures[future] = item

                # 处理结果
                for future in as_completed(futures):
                    # 检查是否应该停止
                    if self.stop_event.is_set():
                        break

                    item = futures[future]
                    try:
                        success = future.result()
                        results.append(
                            {
                                "id": item["id"],
                                "ip": item["ip"],
                                "desc": item["desc"],
                                "result": "成功" if success else "失败",
                            }
                        )
                    except Exception as e:
                        logger.error(f"立即执行任务异常: {str(e)}")
                        results.append(
                            {
                                "id": item["id"],
                                "ip": item["ip"],
                                "desc": item["desc"],
                                "result": "失败",
                            }
                        )

            return {"status": "success", "message": "执行完成", "results": results}
        except Exception as e:
            logger.error(f"立即执行监控项失败: {str(e)}")
            return {"status": "error", "message": f"执行失败: {str(e)}"}

    def get_initial_data(self):
        """获取初始数据供前端界面使用"""
        return {
            "config": self.config_data,
            "stats": self.stats,
            "interval": self.interval,
            "max_workers": self.max_workers,
            "status": "运行中",
            "paused_items": list(self.paused_items),
        }

    def update_settings(self, interval, max_workers):
        """更新监控设置"""
        try:
            interval = int(interval)
            max_workers = int(max_workers)
            if interval <= 0 or max_workers <= 0:
                raise ValueError("输入值必须大于0")

            self.interval = interval
            self.max_workers = max_workers
            self.stop_monitoring()
            self.start_monitoring()
            return {"status": "success", "message": "参数已更新"}
        except Exception as e:
            logger.error(f"更新设置失败: {str(e)}")
            return {"status": "error", "message": f"更新失败: {str(e)}"}

    def stop_monitoring(self):
        """停止监控"""
        if self.monitor_thread and self.monitor_thread.is_alive():
            self.stop_event.set()
            self.monitor_thread.join(timeout=3.0)
            return {"status": "stopped", "message": "监控已停止"}
        return {"status": "already_stopped", "message": "监控未运行"}

    def start_monitoring(self):
        """启动监控"""
        self.stop_event.clear()

        def monitor_task():
            while not self.stop_event.is_set():
                # 检查是否应该停止
                if self.stop_event.is_set():
                    break

                start_time = time.time()

                # 重新加载配置以防有变更
                current_config = self.load_config()

                # 使用线程池执行检测任务
                with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
                    # 检查是否应该停止
                    if self.stop_event.is_set():
                        break

                    futures = {}
                    for idx, item in enumerate(current_config):
                        # 检查是否应该停止
                        if self.stop_event.is_set():
                            break

                        key = str(idx + 1)
                        ip = item["IP"]
                        port = item.get("port", "")
                        desc = item.get("desc", "")

                        # 检查该项目是否被暂停
                        item_id = self._get_item_id_cached(ip, port, desc)
                        if item_id in self.paused_items:
                            continue  # 跳过暂停的项目

                        # 提交任务
                        try:
                            future = executor.submit(
                                self.check_and_update, key, ip, port, desc
                            )
                            futures[future] = key
                        except RuntimeError as e:
                            # 如果解释器正在关闭，退出循环
                            if (
                                "cannot schedule new futures after interpreter shutdown"
                                in str(e)
                            ):
                                logger.info("解释器正在关闭，停止提交新任务")
                                self.stop_event.set()
                                break
                            else:
                                raise e

                    # 处理结果
                    for future in as_completed(futures):
                        # 检查是否应该停止
                        if self.stop_event.is_set():
                            break

                        key = futures[future]
                        try:
                            success = future.result()
                            # 计算成功率
                            with self.lock:
                                # 确保stats中存在该key
                                if key not in self.stats:
                                    self.stats[key] = {
                                        "success": 0,
                                        "failure": 0,
                                        "last_execution": "未执行",
                                        "port": current_config[int(key) - 1].get(
                                            "port", ""
                                        ),
                                        "check_method": (
                                            "Socket"
                                            if current_config[int(key) - 1].get("port")
                                            else "Ping"
                                        ),
                                        "desc": current_config[int(key) - 1].get(
                                            "desc", ""
                                        ),
                                        "ip": current_config[int(key) - 1]["IP"],
                                        "latest_result": "待检测",
                                        "success_rate": 0,
                                    }

                                total = (
                                    self.stats[key]["success"]
                                    + self.stats[key]["failure"]
                                )
                                self.stats[key]["success_rate"] = (
                                    (self.stats[key]["success"] / total) * 100
                                    if total > 0
                                    else 0
                                )
                        except Exception as e:
                            logger.error(f"任务执行异常: {str(e)}")
                            with self.lock:
                                # 确保stats中存在该key
                                if key not in self.stats:
                                    self.stats[key] = {
                                        "success": 0,
                                        "failure": 0,
                                        "last_execution": "未执行",
                                        "port": current_config[int(key) - 1].get(
                                            "port", ""
                                        ),
                                        "check_method": (
                                            "Socket"
                                            if current_config[int(key) - 1].get("port")
                                            else "Ping"
                                        ),
                                        "desc": current_config[int(key) - 1].get(
                                            "desc", ""
                                        ),
                                        "ip": current_config[int(key) - 1]["IP"],
                                        "latest_result": "待检测",
                                        "success_rate": 0,
                                    }

                                self.stats[key]["failure"] += 1
                                self.stats[key]["latest_result"] = "失败"

                # 等待下一轮检测
                elapsed = time.time() - start_time
                wait_time = max(0, self.interval - elapsed)
                if wait_time > 0:
                    # 分段等待，以便能及时响应停止信号
                    waited = 0
                    while waited < wait_time and not self.stop_event.is_set():
                        sleep_time = min(1.0, wait_time - waited)
                        time.sleep(sleep_time)
                        waited += sleep_time

        self.monitor_thread = threading.Thread(target=monitor_task, daemon=True)
        self.monitor_thread.start()
        return {"status": "started", "message": "监控已启动"}

    def _get_item_id_cached(self, ip, port, desc):
        """通过缓存获取项目ID，避免频繁查询数据库"""
        cache_key = (ip, port, desc)

        # 首先尝试从缓存获取
        if cache_key in self.item_id_cache:
            return self.item_id_cache[cache_key]

        # 缓存未命中，查询数据库
        try:
            conn = sqlite3.connect(self.db_file)
            cursor = conn.cursor()

            cursor.execute(
                "SELECT id FROM monitor_items WHERE ip=? AND port=? AND description=?",
                (ip, port, desc),
            )
            row = cursor.fetchone()

            conn.close()

            if row:
                item_id = row[0]
                # 更新缓存
                self.item_id_cache[cache_key] = item_id
                return item_id
            return None
        except Exception as e:
            logger.error(f"获取项目ID失败: {str(e)}")
            return None

    def _get_item_id_by_details(self, ip, port, desc):
        """根据详细信息获取项目ID"""
        return self._get_item_id_cached(ip, port, desc)

    def check_and_update(self, key, ip, port, desc):
        """执行检测并更新数据"""
        # 检查是否应该停止
        if self.stop_event.is_set():
            return False

        try:
            success = self.check_connection(ip, port)
            with self.lock:
                # 确保stats中存在该key
                if key not in self.stats:
                    self.stats[key] = {
                        "success": 0,
                        "failure": 0,
                        "last_execution": "未执行",
                        "port": port,
                        "check_method": "Socket" if port else "Ping",
                        "desc": desc,
                        "ip": ip,
                        "latest_result": "待检测",
                        "success_rate": 0,
                    }

                if success:
                    self.stats[key]["success"] += 1
                    self.stats[key]["latest_result"] = "成功"
                else:
                    self.stats[key]["failure"] += 1
                    self.stats[key]["latest_result"] = "失败"

                self.stats[key]["last_execution"] = time.strftime("%Y-%m-%d %H:%M:%S")
            return success
        except Exception as e:
            logger.error(f"检测异常: {str(e)}")
            with self.lock:
                # 确保stats中存在该key
                if key not in self.stats:
                    self.stats[key] = {
                        "success": 0,
                        "failure": 0,
                        "last_execution": "未执行",
                        "port": port,
                        "check_method": "Socket" if port else "Ping",
                        "desc": desc,
                        "ip": ip,
                        "latest_result": "待检测",
                        "success_rate": 0,
                    }

                self.stats[key]["failure"] += 1
                self.stats[key]["latest_result"] = "失败"
            return False

    def check_connection(self, ip, port):
        """执行网络检测"""
        # 检查是否应该停止
        if self.stop_event.is_set():
            return False

        if port:
            return self.check_port(ip, port)
        return self.ping(ip)

    def ping(self, ip):
        """Ping检测"""
        try:
            param = "-n 1" if sys.platform.startswith("win") else "-c 1"
            subprocess.check_output(
                f"ping {param} {ip}", shell=True, stderr=subprocess.STDOUT, timeout=5
            )
            return True
        except Exception:
            return False

    def check_port(self, ip, port):
        """端口检测"""
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.settimeout(5)
                s.connect((ip, int(port)))
                return True
        except Exception:
            return False

    def cleanup(self):
        """清理资源"""
        logger.info("正在清理资源...")
        self.stop_monitoring()


def create_webview_window():
    """创建WebView窗口"""
    # 获取资源文件路径，兼容打包后的环境
    if getattr(sys, 'frozen', False):
        # 打包后的环境
        if sys.platform == 'darwin' and '.app' in sys.executable:
            # macOS .app 包环境
            current_dir = Path(sys.executable).parent.parent / "Resources"
        else:
            # 其他打包环境
            current_dir = Path(sys.executable).parent
    else:
        # 开发环境
        current_dir = Path(__file__).parent

    # 创建API实例
    api = PyWebViewApi()
    
    # 检查UI文件是否存在
    ui_path = current_dir / "ui" / "index.html"
    logger.info(f"UI文件路径: {ui_path}")
    logger.info(f"UI文件是否存在: {ui_path.exists()}")
    
    if not ui_path.exists():
        logger.error(f"UI文件不存在: {ui_path}")
        # 尝试其他可能的路径
        alternative_paths = [
            Path(sys.executable).parent / "ui" / "index.html",
            Path(sys.executable).parent / "_internal" / "ui" / "index.html",
            current_dir / "_internal" / "ui" / "index.html"
        ]
        for alt_path in alternative_paths:
            logger.info(f"尝试路径: {alt_path}, 存在: {alt_path.exists()}")
            if alt_path.exists():
                ui_path = alt_path
                break

    # 创建窗口
    window = webview.create_window(
        "网络监控工具V1.0",
        url=str(ui_path),
        js_api=api,
        width=1200,
        height=800,
        min_size=(800, 600),
        text_select=True,
    )

    return window


if __name__ == "__main__":
    # 创建并启动窗口
    window = create_webview_window()

    try:
        webview.start()
    except KeyboardInterrupt:
        logger.info("收到键盘中断信号，正在退出...")
    finally:
        logger.info("程序已退出")
